package com.casit.base.service;

import com.casit.base.entity.BaseEntity;
import com.casit.base.entity.IdEntity;
import com.casit.base.exception.ResultException;
import com.casit.base.jpa.BaseRepository;
import com.casit.base.jpa.DynamicSpecifications;
import com.casit.base.util.UserUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.Serializable;
import java.util.Date;
import java.util.Optional;
import java.util.UUID;

public class BaseService<Entity, ID extends Serializable> {

    protected final static Logger logger = LoggerFactory.getLogger(BaseService.class);

    protected BaseRepository<Entity, ID> jpaRepository;

    public BaseService(BaseRepository<Entity, ID> jpaRepository) {
        this.jpaRepository = jpaRepository;
    }

    /**
     * 查询所有数据
     *
     * @param pageable 分页信息
     * @param search   查询过滤条件
     * @param self     是否只查本人的
     * @param valid    是否只查有效数据
     * @return
     */
    public Page<Entity> getList(Pageable pageable, String search, Boolean self, Boolean valid) {
        return jpaRepository.findAll(new Specification<Entity>() {
            @Override
            public Predicate toPredicate(Root<Entity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Predicate predicate = DynamicSpecifications.buildPredicate(search, root, criteriaQuery, criteriaBuilder);
                if (valid) {
                    predicate = predicate == null ? DynamicSpecifications.normalPredicate(BaseEntity.FLAG_NORMAL_, root, criteriaQuery, criteriaBuilder) : criteriaBuilder.and(predicate, DynamicSpecifications.normalPredicate(BaseEntity.FLAG_NORMAL_, root, criteriaQuery, criteriaBuilder));
                }


                if (self) {
                    predicate = predicate == null ? DynamicSpecifications.createByPredicate(UserUtil.getCurrentUserId(), root, criteriaQuery, criteriaBuilder) : criteriaBuilder.and(predicate, DynamicSpecifications.createByPredicate(UserUtil.getCurrentUserId(), root, criteriaQuery, criteriaBuilder));

                }
                return predicate;
            }
        }, pageable);
    }


    @Transactional
    public Entity save(Entity entity) {
        //如果是基于IDEntity.且没有生成对应的ID，自动生成一个UUID
        if (entity instanceof IdEntity) {
            IdEntity<String> id = (IdEntity<String>) entity;
            if (id != null && StringUtils.isEmpty(id.getId())) {
                logger.trace("自动生成UUID主键");
                id.setId(UUID.randomUUID().toString().replace("-", ""));
            }
        }
        return jpaRepository.save(entity);
    }

    public Entity get(ID id) {
        Optional<Entity> entity = this.jpaRepository.findById(id);
        if (!entity.isPresent()) {
            logger.info("未找到需要更新的原始记录");
            throw new ResultException(-1, "未找到对应记录");
        }
        return entity.get();
    }

    public Entity update(ID id, Entity entity) {

        Optional<Entity> oldEntity = this.jpaRepository.findById(id);
        if (!oldEntity.isPresent()) {
            logger.info("未找到需要更新的原始记录");
            throw new ResultException(-1, "未找到对应记录");
        }
        //全量更新
        return oldEntity.get();

    }

    public void delete(ID id) {
        jpaRepository.deleteById(id);
    }

    @Transactional
    public void softDelete(ID id) {
        Optional<Entity> entity = this.jpaRepository.findById(id);
        if (entity.isPresent()) {
            if (entity.get() instanceof BaseEntity) {
                logger.debug("进行逻辑删除");
                BaseEntity be = (BaseEntity) entity.get();
                be.setxFlag(BaseEntity.FLAG_DELETE_);
                be.setxDeleteDate(new Date());
            }

        }
    }
}
